local license =
[[----------------------------------------------------------------------
  **** BEGIN LICENSE BLOCK ****
	A simple flocking simulator written in LUA.

    Copyright (C) 2010
	Eric Fredericksen
	www.pttpsystems.com
	eric@pttpsystems.com

	This file is part of Flocker.

    Flocker is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

  **** END LICENSE BLOCK ****
------------------------------------------------------------------------]]

require("flockerutils")


--[[ See http://en.wikipedia.org/wiki/OODA_loop
		"The OODA loop (for Observe, orient, decide, and act) is a concept originally
		applied to the combat operations process, often at the strategic level in
		both the military operations. It is now also often applied to understand
		commercial operations and learning processes. The concept was developed by
		military strategist and USAF Colonel John Boyd."

	Birds are state driven animals. The state API includes the following methods.

	StartState( self, optionalParameters )	-- do setup
	EndState(   self )						-- do teardown

	Reset_OODA( self ) 						-- reset facts and calculations for decision making
	Observe( self, list_of_objects )		-- Observe surroundings and accumulate facts (multiiple calls)
	OrientDecideAct( self )					-- change direction, take action, change state

	GetDisplayList( self, primitives )		-- offer up bird's state-related display primitives (lines, circles)
		primitives = { lines={}, circles={} }
		line   = { x1, y1, x2, y2, w, c }	-- insert your entries into this table
		circle = { x,  y,          r, c }	-- insert your entries into this table

	There is also an inter-bird communications network. The functions supplied are
	bird:SendMessage( message={name="??", ... } )	-- add to end of Q
	bird:PushMessage( message={name="??", ... } )	-- push to front of Q
	bird:PopMessage( )								-- removes and returns message from front of Q
	bird:FindMessage( filterFn )					-- remove and return the first message that passes the filter
]]


--[[
==============================================================================================
============================== STATE DEFS BELOW ==============================================
==============================================================================================
]]

-- global scope object for our code; let's keep things tidy
assert (flocker, "Flocker Engine: Failed to find global variable flocker")

-- collector for state API impelmentations
flocker.states = {}


--[[================================= COMMON PARAMETERS ======================================]]

-- parameters for use by states; may come back and refactor these into namespaces later
flocker.states.parameters =
	{
		age =
		{
			  start			= 1		-- milliseconds
			, adult 		= 30	-- age (s) at which growth stops (health increases until)
			, mature	 	= 60	-- age (s) past which birds are in "middle age"
			, old		 	= 120	-- age (s) past which birds health starts declining
			, growthRate 	= 1.01	-- rate of health increase per iteration
			, declineRate 	= 0.99	-- rate of health decrease per iteration
			, white			= {	1.0, 1.0, 1.0, 1 }
			, gray			= {	0.5, 0.5, 0.5, 1 }
			, black 		= {	0.0, 0.0, 0.0, 1 }
		}
		, health =
		{
			  start			= 1000		-- at birth
			, max			= 10000		-- cap on health
			, min			= 200 		-- dead below this HP count
		}
		, preg =
		{
			  critical		= 3			-- local population required to spawn
			, gestation 	= 10*1000	-- (ms) duration of pregnancy
			, refractory	= 10*1000	-- (ms) post-pregnancy infertility period
			, circle		= 7			-- size in pixels
		}
		, death =
		{
			  circle 		= 7		-- size in pixels
			, decay			= 5*1000	-- milliseconds until body disappears
		}
		, flee =
		{
			  burstT		= 3*1000	-- ms burst of speed followed by slowdown; raised sinusoid
			, burstS		= 1.0		-- speed burst; multiplier for deltPX
		}
		, fight =
		{
			  burstT		= 2*1000	-- ms burst of speed followed by slowdown; raised sinusoid
			, burstS		= 0.7		-- speed burst; multiplier for deltPX
			, biteSize		= 4000		-- DPS of damage
		}
	}

-- fill this in with desired colors
flocker.colorMap =
	{
		  [1]={ 0.00,	0.00,	0.00, 1,	n="BLACK"	}
		, [2]={ 0.33,	0.33,	0.33, 1,	n="DKGRAY"	}
		, [3]={ 0.50,	0.50,	0.50, 1,	n="GRAY"	}
		, [4]={ 0.66,	0.66,	0.66, 1,	n="LTGRAY"	}
		, [5]={ 1.00,	1.00,	1.00, 1,	n="WHITE"	}
	}
-- default color for the background - index into above array
flocker.bgColor = 1


--[[================================= INTER-BIRD MESSAGING FUNCTIONS ==================================]]
function flocker.states.SendMessage( self, message )
	flocker.assert( message and "table" == type(message), "flocker.states.SendMessage: invalid message" )
	table.insert(self.messageQueue, message)
end


function flocker.states.PushMessage( self, message )
	flocker.assert( message and "table" == type(message), "flocker.states.PushMessage: invalid message" )
	table.insert(self.messageQueue, 0, message)
end


function flocker.states.PopMessage( self )
	return table.remove(self.messageQueue, 0)
end


function flocker.states.FindMessage( self, filterFn )
	flocker.assert( filterFn, "flocker.states.FindMessage: missing filter function. Use PopMessage instead" )

	for k, m in ipairs( self.messageQueue ) do
		if filterFn( m ) then table.remove( self.messageQueue, k ) end
		return m
	end

	return nil
end


--[[================================= COMMON FUNCTIONS ======================================]]

-- automatically decorate the bird with functions provided by the assigned state
function flocker.states.SetState( self, state, optionalParameters )

	-- take care of teardown unless this is the first time we are building a bird
	if self.EndState then self:EndState() end

	assert( "string" == type(state), "flocker.states.SetState: invalid state object" )
	state = flocker.states[state]
	assert( state, "flocker.states.SetState: invalid state name" )

	-- add the state functions to the bird for simple access
	self.state = state
	for name, fn in pairs( state ) do
		if "function" == type(fn) then self[name] = fn end
	end

	-- take care of startup
	self:StartState( optionalParameters ) -- same as state.StartState(self)

end

--[[Used within a state to request a state change on the next iteration
	Optional parameters get passed through the StartState function. That allows us
	to avoid haphazard parameter caching on the body of the bird.
]]
function flocker.states.SetNextState(self, stateName, optionalParameters)
	self.__nextState = { state = stateName, parameters = optionalParameters }
end


--[[ Common code to show a bird and its repel/attract areas
]]
function flocker.states.ShowBird( self, primitives )

	-- here are the OGL driven changes that will replace most, if not all, of the above
	ogl3D = self.idle.ogl3D
--[[{
		  p = vec3 -- my position
		, head = { p=vec3, r=int, 	w=int,	c=vec3 }
		, body = { v=vec3, 			w=int,  c=vec3 } body vector drawn from head
	}
]]

	vec3.copyTo(	ogl3D.p, self.p	)

	local lengthVec = vec3.newV( self.d )
	vec3.scaleIt( lengthVec, self.ageLength )

	vec3.copyTo( ogl3D.head.p, self.p )
	vec3.addTo( ogl3D.head.p, lengthVec )
	ogl3D.head.r = self.ageHeadSize
	ogl3D.head.w = self.ageWidth

	vec3.copyTo( ogl3D.head.c, self.color )

	-- body vector is drawn from head to tail
	vec3.scaleIt( lengthVec, -2 )
	vec3.copyTo( ogl3D.body.v, lengthVec )
	ogl3D.body.w = self.ageWidth
	vec3.copyTo( ogl3D.body.c, self.agecolor )

	table.insert( primitives.birds, ogl3D )


end


--[[ Common code to update state data of a bird based on its age
]]
function flocker.states.AgeBird( self )

	local params = flocker.states.parameters

	-- age the bird
	self.a = self.a + flocker.deltaT

	-- convert age to seconds
	local seconds = self.a/1000

	-- some defaults
	local alpha = 1.0
	local blendColor = self.color
	if seconds > params.age.old then
		-- bird is losing health and stamina
		self.age 			= "declining"
		self.h 				= math.min(params.health.max, self.h * params.age.declineRate)
		self.ageLength 		= 13
		self.ageHeadSize 	= 6
		self.ageWidth 		= 2
		blendColor 			= params.age.black
		self.fertile		= false
		self.aggressive		= false
		alpha 				= 0.5

	elseif seconds > params.age.mature then
		-- bird is in the prime of life; enable repro, disable attack
		self.age 			= "mature"
		self.ageLength 		= 16
		self.ageHeadSize 	= 7
		self.ageWidth 		= 3
		alpha 				= 1.0
		blendColor 			= params.age.white
		self.fertile		= true
		self.aggressive		= false
	elseif seconds > params.age.adult then
		-- bird is a young adult; enable attack of enemies
		self.age 			= "adult"
		self.ageLength 		= 11
		self.ageHeadSize 	= 6
		self.ageWidth 		= 3
		alpha 				= 0.75
		blendColor 			= params.age.white
		self.fertile		= false
		self.aggressive		= true
	else
		-- bird is growing
		self.age 			= "young"
		self.h 				= math.min(params.health.max, self.h * params.age.growthRate)
		self.ageLength 		= 8
		self.ageHeadSize 	= 5
		self.ageWidth 		= 2
		alpha 				= 0.25
		blendColor 			= params.age.white
		self.fertile		= false
		self.aggressive		= false
	end

	if not self.agecolor then self.agecolor = {1,1,1,1} end

	vec3.copyTo( self.agecolor, self.color)
	vec3.alphaBlendIt( self.agecolor, blendColor, alpha )

	--self.agecolor[1] = math.min( 1, self.color[1]*alpha + (1-alpha)*blendColor[1] )
	--self.agecolor[2] = math.min( 1, self.color[2]*alpha + (1-alpha)*blendColor[2] )
	--self.agecolor[3] = math.min( 1, self.color[3]*alpha + (1-alpha)*blendColor[3] )

	-- "I'm not dead yet! I'm happy!"
	if self.state.name ~= "dead" and self.h < params.health.min then
		self:SetNextState( "dead" )
	end

end


--[[Commmon code for adjusting direction based on agility
	Tilt direction by indicated factor relative to current direction
	using the indicated "rate of turn" (sort of, given that vector
	addition is non uniform direction change)
	]]
function flocker.states.AdjustDirection(self, targetDir )

	vec3.normalizeIt( targetDir )
	vec3.scaleIt( targetDir, flocker.agility * flocker.deltaT/1000 )
	vec3.addTo( self.d, targetDir )
	vec3.normalizeIt( self.d )

end


-- common code for heading for a given position
function flocker.states.TrackPosition(self, position, speedMult )

	local targetDir = vec3.sub(position, self.p)

	local distance = vec3.length( targetDir )

	self:AdjustDirection( targetDir )
	self:MoveBird( speedMult )

	return distance
end



--[[Common code to select the  item in a list nearest to self.
	filterFn takes an object reference and returns true/false
]]
function flocker.states.FindNearest( self, list, filterFn )

	assert( "table" == type(list), "flocker.states.FindNearest: invalid list" )

	-- there is no way to predict optimal order of filter or distance test, but it is simple to do this first
	local filteredList = filterFn and FilterList( list, filterFn ) or list -- funky LUA ternary :)
	-- make the start value way big
	local metric = math.huge
	local nearest = nil
	for __, listItem in pairs( filteredList ) do

		local distance2 = vec3.distanceSquared( listItem.p, self.p )
		if distance2 < metric then
			nearest = listItem
			metric  = distance2
		end

	end

	-- turn d^2 into d
	return nearest, metric^0.5
end

-- special purpose reflect birds from boundaries of their box
function flocker.states.ReflectBirdDirection( bird )

	local bx, by, bz = flocker.toroidX, flocker.toroidY, flocker.toroidZ

	if bird.p[1] < 0 then
		bird.p[1] = -bird.p[1]
		bird.d[1] = -bird.d[1]

	elseif bird.p[1] > bx 	then

		bird.p[1] = 2*bx - bird.p[1]
		bird.d[1] = -bird.d[1]
	end

	if bird.p[2] < 0  then
		bird.p[2] = -bird.p[2]
		bird.d[2] = -bird.d[2]

	elseif bird.p[2] > by then

		bird.p[2] = 2*by - bird.p[2]
		bird.d[2] = -bird.d[2]
		end

	if bird.p[3] < 0 then
		bird.p[3] = -bird.p[3]
		bird.d[3] = -bird.d[3]

	elseif bird.p[3] > bz then
		bird.p[3] = 2*bz - bird.p[3]
		bird.d[3] = -bird.d[3]
	end

end


-- function to take one incremental step, and to handle a caught bird being dragged by the cursor
function flocker.states.MoveBird( self, speedModifier )

	speedModifier = speedModifier or 1.0


	if self.caught then
		vec3.copyTo(	self.p, flocker.cursorPos)
		vec3.jitterPos( self.p )
		vec3.copyTo(	self.d, flocker.cursorDir)
	else
		-- we added a propensity to look around and investigate :)
		-- with 3D flocking we need to add wiggle out of the x-y plane
		local halfCaff = flocker.caffeine/2
		if flocker.threeDeeFlocking then
			local randR = math.random( -halfCaff, halfCaff )
			local randP = math.random( -halfCaff, halfCaff )
			local randY = math.random( -halfCaff, halfCaff )
			vec3.rotDegIt( self.d, randR, randP, randY )

		else -- 2D is only random twitch around z-axis
			local randRoll = math.random( -halfCaff, halfCaff )
			vec3.rotDegIt( self.d, randomRoll, nil, nil )

		end

		-- this function is only called if the simulation is running
		local stepVector = vec3.scale(self.d, flocker.deltaPX * speedModifier ) -- not inplace
		vec3.addTo(  self.p, stepVector) -- inplace

		if flocker.reflectBirds then
			flocker.states.ReflectBirdDirection( self ) -- inplace
		else
			vec3.wrapIt( self.p, flocker.toroidX, flocker.toroidY, flocker.toroidZ) -- inplace
		end
	end

end

--[[============================== IDLE STATE ====================================================]]

flocker.states.idle = { name = "idle" }

function flocker.states.idle.StartState( self, parameters )

	-- namespace
	self.idle = {}

	-- reset refractory
	self.idle.refractory = flocker.states.parameters.preg.refractory

	-- use a property as a singleton flag so  we don't do this *every* time we change state
	if not self.idle.data then

		-- OGL 3D primitives
		self.idle.ogl3D =
		{
			  p = vec3.new(1,1,1)
			, head = { p=vec3.new(0,0,0), w=1,r=20, c=vec3.new(1,1,0) }
			, body = { v=vec3.new(0,0,0), w=1,   	c=vec3.new(1,0,1) }
		}

		self.idle.data =
		{
			  p 	= vec3.new(0,0,0)	-- average neighbor position, including self
			, count	= 0 				-- neighbor count, including self, for calculating COM for attraction
			, d 	= vec3.new(0,0,0)	-- average neighbor direction, including self
			, r 	= vec3.new(0,0,0)	-- repel direction accumulator

			-- must invoke this as bird.data:reset()
			, reset = function(self)
				vec3.set(self.p, 0, 0, 0)
				self.count = 0
				vec3.set(self.d, 0, 0, 0)
				vec3.set(self.r, 0, 0, 0)
			end
		}
	end

end

function flocker.states.idle.EndState( self )
	-- don't clean up, other states (like pregnant) depend on these data
end


-- idle state will be flocking
function flocker.states.idle.Reset_OODA( self )

	-- create accounting structures if necessary
	self.idle.nearbyBirds	= {}
	self.idle.foreignBirds	= {}
	self.idle.likeBirds		= {}
	self.idle.fnLiveAdults	=
		function( bird ) return ("adult" == bird.age) and ("dead" ~= bird.state.name) end

	self.idle.data:reset()
end

function flocker.states.idle.Observe( self, list )

	for __, otherBird in pairs(list) do
		-- reference comparison is explicit in LUA: same object in memory
		if not(otherBird == self) then

			local vectorFromOtherToThis = vec3.sub(self.p, otherBird.p)

			local distance = vec3.length( vectorFromOtherToThis )

			-- if we have a hit
			if( distance < flocker.detectR ) then
				-- save for later
				table.insert(self.idle.nearbyBirds, otherBird)

				local data = self.idle.data

				-- keep a list of the foreign birds
				if self.typeId ~= otherBird.typeId then
					table.insert( self.idle.foreignBirds, otherBird)
				else -- my current flock
					table.insert( self.idle.likeBirds, otherBird)
				end

				if not(flocker.partisan) or self.typeId == otherBird.typeId then
					-- accumulate POSITION data for OTHER bird
					vec3.addTo( data.p, otherBird.p )
					data.count = data.count + 1

					-- accumulate DIRECTION data for OTHER bird
					vec3.addTo( data.d, otherBird.d )
				end

				if( distance < flocker.repulseR ) then
					-- we need to repel these two birds from each other

					vec3.normalizeIt( vectorFromOtherToThis )

					vec3.addTo( data.r, vectorFromOtherToThis )
				end
			end
		end
	end
end


function flocker.states.idle.OrientDecideAct( self )

	local data = self.idle.data
	-- the target direction is just my direction
	local targetDir = self.d

	-- should we flock towards other birds
	if( vec3.isNotNull( data.p ) ) then

		-- accumulate POSITION data for THIS bird
		vec3.addTo( data.p, self.p )
		data.count = data.count + 1

		-- accumulate DIRECTION data for THIS bird
		vec3.addTo( data.d, self.d )

		-- normalize center-of-mass direction vector
		-- note that we can't do this earlier because we have to accumulate ALL the positions
		-- note also that these are positions, not vectors, so we don't use vec3.normalizeIt (yet)
		local comP = vec3.scaleIt( data.p, 1/data.count )

		-- compute the vector to center-of-mass of the influential neighbors from this bird
		local dirC = vec3.sub( comP, self.p )

		-- create average-local-direction unit vector
		local codD = vec3.normalizeIt( data.d )

		-- alpha for this bird position to COM
		local alpha = ( vec3.length( dirC ) - flocker.repulseR ) / flocker.detectR
		if alpha < 0 then alpha = 0 end

		-- scale the two vectors for convex combination
		vec3.scaleIt( dirC, alpha )
		vec3.scaleIt( codD, (1-alpha) )

		-- add them in convex combination
		targetDir = vec3.add( dirC, codD )
	end

	-- if there were birds too close
	if( vec3.isNotNull( data.r ) ) then

		-- create repel-direction unit vector
		local codR = vec3.normalizeIt( data.r )

		-- strength for repel
		vec3.scaleIt( codR, flocker.antisociality )

		-- add in repel direction
		vec3.addTo( targetDir, codR )
	end

	self:AdjustDirection( targetDir )
	self:MoveBird()


	local message = self:FindMessage( function( msg ) return "attack" == msg.name end )
	if message then -- i am under attack!

		local myGang = FilterList( self.idle.likeBirds, filter)
		self:SetNextState( "flee", { attacker = message.attacker, likeBirds = myGang } )

	-- if there is someone for me to attack
	elseif "adult" == self.age and flocker.aggressive then

		local otherGang, ogCount	= FilterList( self.idle.foreignBirds, self.idle.fnLiveAdults )
		local myGang, mgCount 		= FilterList( self.idle.likeBirds,    self.idle.fnLiveAdults )

		if (ogCount > 0) and (mgCount > 0) and (mgCount > ogCount) then
			-- we have them outnumbered, boys!
			self:SetNextState( "fight",
				{ foreignBirds = otherGang, likeBirds = myGang, ogc = ogCount, mgc=mgCount }
				)
		end

	-- if bird is fertile (old enough) and not already pregnant
	elseif ("mature" == self.age) and (not self.pregnant) then

		-- if local population is high enough
		if data.count >= flocker.states.parameters.preg.critical then

			-- decrement refractory
			self.idle.refractory = self.idle.refractory - flocker.deltaT
			if( self.idle.refractory < 0 ) then
				local otherParent = self:FindNearest( self.idle.nearbyBirds )
				flocker.assert( otherParent, "There seems to be an absentee parent")
				self:SetNextState( "pregnant", { geneDonor = otherParent } )
			end

		else -- need to reset refractory period; population too low
			self.idle.refractory = flocker.states.parameters.preg.refractory
		end

	end

end


--show the bird with default display
function flocker.states.idle.GetDisplayList( self, primitives )
	self:ShowBird( primitives )
end



--[[============================== PREGNANT STATE ================================================]]
-- pregnancy is a sub-state of idle (flocking), sort of
flocker.states.pregnant = { name = "pregnant" }

function flocker.states.pregnant.StartState( self, parameters )
	self.fertile 	= false
	-- use a namespace
	self.pregnant	= {}
	self.pregnant.gestation = flocker.states.parameters.preg.gestation

	self.idle.ogl3D.aura = { w=2, r, c={ 0,1,1 } }

	flocker.assert( parameters and parameters.geneDonor, "flocker.states.pregnant.StartState: absent gene donor" )
	local geneDonor = parameters.geneDonor

	if geneDonor.typeId == self.typeId then
		self.pregnant.babyTypeId = self.typeId
	else -- spawning a hybrid
		self.pregnant.babyTypeId = flocker.birdTypes.hybridId
	end

end

function flocker.states.idle.EndState( self )
	self.fertile  	= true
	self.refractory	= flocker.states.parameters.preg.refractory
	self.pregnant 	= nil -- cleanup
	self.idle.ogl3D.aura = nil
end

function flocker.states.pregnant.Reset_OODA( self )
	-- idle OODA loop
	flocker.states.idle.Reset_OODA( self )
end


function flocker.states.pregnant.Observe( self, list )
	-- idle OODA loop
	flocker.states.idle.Observe( self, list )
end


function flocker.states.pregnant.OrientDecideAct( self )

	-- idle OODA loop
	flocker.states.idle.OrientDecideAct( self )

	self.pregnant.gestation = self.pregnant.gestation - flocker.deltaT

	-- time to give birth and change states
	if self.pregnant.gestation < 0 then

		-- we can probably do something more insteresting with genetics here
		local babyType = flocker.birdTypes[self.pregnant.babyTypeId]

		-- place the new one right next to me and moving my direction
		flocker.createNewBird( babyType, vec3.newV(self.p), vec3.newV(self.d) )

		-- switch back to idle state
		self:SetNextState( "idle" )
	end
end


function flocker.states.pregnant.GetDisplayList( self, primitives )

	--local primitives = {lines={}, circles={} }
	local scale 		= flocker.states.parameters.preg.gestation
	local pregCircle 	= flocker.states.parameters.preg.circle

	-- grow the pregnancy circle over time
	local radius 		= pregCircle*(scale - self.pregnant.gestation)/scale

	-- add this to our data before allowing the idle state to insert into the list
	local aura = self.idle.ogl3D.aura
	-- ensure the circle has a minimum size to be visible
	aura.r = math.max(2, radius )

	-- we just add something to the standard display
	self:ShowBird( primitives )
end


--[[============================== DEAD STATE ====================================================]]

flocker.states.dead = { name = "dead" }

-- when you are dead you don't do much

function flocker.states.dead.StartState( self, parameters )

	self.dead = {}
	self.dead.decay = flocker.states.parameters.death.decay

	--local primitives = {lines={}, circles={}, birds={}, deaders={} }
	local d = flocker.states.parameters.death.circle/2
	self.dead.deader =
	{
		  p=self.p
		, r=flocker.states.parameters.death.circle
		, w=2
		, c=self.agecolor
		, line1 = { s={ d,  d }, e={ -d, -d }, c={1,1,1,1} }
		, line2 = { s={ d, -d }, e={ -d,  d }, c={1,1,1,1} }
	}
end

function flocker.states.dead.EndState( self )
	self.dead = nil
end

function flocker.states.dead.Reset_OODA( self ) end
function flocker.states.dead.Observe( self, list ) end
function flocker.states.dead.OrientDecideAct( self )

	-- decrement timer
	self.dead.decay = self.dead.decay - flocker.deltaT

	if self.dead.decay < 0 then
		-- clean me up
		flocker.RemoveBird( self )
	end
end

function flocker.states.dead.GetDisplayList( self, primitives )

	local alpha = 1*self.dead.decay/flocker.states.parameters.death.decay
	self.agecolor[4] = alpha
	self.dead.deader.line1.c[4] = alpha
	self.dead.deader.line2.c[4] = alpha
	table.insert(primitives.deaders,	self.dead.deader )
end


--[[============================== FIGHT STATE ====================================================]]

flocker.states.fight = { name = "fight" }

-- this bird is angry and wants blood

function flocker.states.fight.StartState( self, parameters )
	--[[ parameters = { foreignBirds={}, likeBirds = {} }
			foreignBirds -- my enemies
			likeBirds -- my flock
	]]

	-- use a namespace
	self.fight = {}
	self.fight.timer = 0
	self.fight.biting = false

	-- pick out a target, the nearest enemy
	self.fight.target	= self:FindNearest( parameters.foreignBirds )

	flocker.assert(
		  self.fight.target
		, "flocker.states.fight.startState: could not find a target"
		, function()
				edump("no target: parameters", parameters )
				edump("no target: foreignBirds", parameters.foreignBirds )
			end
		)

	-- let this guy know we are attacking him
	self.fight.target:SendMessage( {name="attack", attacker=self } )

	-- pick the nearest friend to return to when we are done
	self.fight.homebase	= self:FindNearest( parameters.likeBirds )
	flocker.assert(
		  self.fight.homebase
		, "flocker.states.fight.startState: could not find a homebase"
		, function()
				edump("no target: parameters", parameters )
				edump("no target: foreignBirds", parameters.likeBirds )
			end
		  )

	-- indicator that I am in a fight, and where my target is
	self.fight.laser= { s=vec3.new(0,0,0), e=vec3.new(1,1,1), w=3, c=self.color, ce={1,1,1,1} }
end

function flocker.states.fight.EndState( self )
	-- cleanup
	self.fight		= nil
end

function flocker.states.fight.Reset_OODA( self ) --[[ don't care ]] end
function flocker.states.fight.Observe( self, list ) --[[ don't care ]] end


function flocker.states.fight.OrientDecideAct( self )

	--[[	(a) chase the target, putting on a burst of speed
			(b) bite the target (decrement health) until it goes out of range
			(c) burst of speed causes eventual slowdown
			(d) once out of range, [try to] return to the original flock
	]]

	local fight = self.fight
	fight.timer = fight.timer + flocker.deltaT	-- increment timer

	local burstT  = flocker.states.parameters.fight.burstT
	local burstT2 = burstT*3

	local burstS  = flocker.states.parameters.fight.burstS

	-- check for death
	if (fight.timer < burstT) and ("dead" ~= fight.target.state.name) then
		-- still pursuing
		local speedMult = 1.0 + burstS * RaisedCosine( fight.timer, burstT )

		local distance =  self:TrackPosition( fight.target.p, speedMult )

		if distance > flocker.detectR then
			-- force return - the quarry is too far away
			fight.timer = burstT2
			fight.biting = false
		elseif distance < flocker.repulseR then
			-- time to bite
			fight.biting = true
			-- scale to HP per second, or DPS
			local biteSize = flocker.deltaT/1000 * flocker.states.parameters.fight.biteSize
			fight.target.h = fight.target.h - biteSize
		end

	elseif fight.timer < burstT2 then

		--[[Heading back to the roost for some interval,
			wobbling between slower and faster than normal
			]]
		fight.biting = false
		local speedMult = 1.0 + burstS * RaisedCosine( fight.timer, burstT2 )
		local distance =  self:TrackPosition( fight.homebase.p, speedMult )

		if distance < flocker.detectR then
			-- time to go back to idle
			self:SetNextState( "idle" )
		end
	else
		-- time to go back to idle state and look for friends
		self:SetNextState( "idle" )
	end

end


function flocker.states.fight.GetDisplayList( self, primitives )

	-- we may add something to the standard display to represent the byte
	self:ShowBird( primitives )

	local distance =  vec3.distance( self.fight.target.p, self.p )
	-- enable laser
	if self.fight.biting and (distance < flocker.detectR) then

		vec3.copyTo( self.fight.laser.s, self.p )

		vec3.copyTo( self.fight.laser.e, self.fight.target.p )

		table.insert( primitives.lines, self.fight.laser )
	end

end


--[[============================== FLEE STATE ===================================================]]

flocker.states.flee = { name = "flee" }

-- this bird is being attacked and chose the better part of valor

function flocker.states.flee.StartState( self, parameters )
	--[[ parameters should include:
			the attacker bird(S)
			data to be used to track original flock; if i survive I'll go back
	]]
	-- add namespace
	self.flee = {}
	self.flee.timer = 0
	self.flee.attacker = parameters.attacker

	-- pick the nearest friend to return to when we are done
	-- there might not be one
	self.flee.homebase = self:FindNearest( parameters.likeBirds )

end

function flocker.states.flee.EndState( self )
	self.flee = nil -- cleanup
end

function flocker.states.flee.Reset_OODA( self )
	-- check for more attackers here and change to the new one
	-- this may be taken care of by reentering attack from idle
end

function flocker.states.flee.Observe( self, list ) end

function flocker.states.flee.OrientDecideAct( self )

	local flee = self.flee
	flee.timer = flee.timer + flocker.deltaT

	local burstT  = flocker.states.parameters.flee.burstT
	local burstT2 = burstT*2
	local burstS  = flocker.states.parameters.flee.burstS

	if flee.timer < burstT then

		-- we need to run away from the given attacker
		local fleeDirection = vec3.sub( self.p, flee.attacker.p )

		self:AdjustDirection( fleeDirection )

		local speedMult = 1.0 + burstS * RaisedCosine( flee.timer, burstT )

		self:MoveBird(speedMult)

	elseif flee.homebase and flee.timer < burstT2 then

		--[[Heading back to the roost for some interval,
			wobbling between slower and faster than normal
			]]
		local speedMult = 1.0 + burstS * RaisedCosine( flee.timer, burstT )

		local distance =  self:TrackPosition( flee.homebase.p, speedMult )

		if distance < flocker.detectR then
			-- time to go back to idle
			self:SetNextState( "idle" )
		end
	else
		self:SetNextState( "idle" )
	end

end

function flocker.states.flee.GetDisplayList( self, primitives )
	-- we ,ay add something to the standard display
	self:ShowBird( primitives )
end


--[[============================== INITIALIZATION AND BIRD CREATION =================================]]

flocker.birdTypes =
	{
		  [1] = {
			  typeId				= 1
			, typeName 				= "type1"
			, color 				= { 0, 0, 0.5, 1 }
			, initialState			= "idle"	-- initial state
			, optionalParameters	= nil		-- in case we want to start in a state with configurability
		}
		, [2] = {
			  typeId				= 2
			, typeName 				= "type2"
			, color 				= { 0.5, 0, 0, 1 }
			, initialState			= "idle"	-- initial state
			, optionalParameters	= nil		-- in case we want to start in a state with configurability
		}
		, [3] = {
			  typeId				= 3
			, typeName				= "hybrid"
			, color 				= { 0.5, 0, 0.5, 1 }
			, initialState			= "idle"	-- initial state
			, optionalParameters	= nil		-- in case we want to start in a state with configurability
		}
	}
flocker.birdTypes.hybridId = 3


function flocker.RemoveBird( thisBird )
	assert( thisBird and flocker.birds[thisBird], "flocker.RemoveBird: invalid argument")
	thisBird:EndState()
	flocker.birds[thisBird] = nil
	flocker.birdCount = flocker.birdCount - 1
end


function flocker.createNewBird( birdType, position, direction, health, age )

	if position then
		position =  vec3.newV(position) -- make a copy!
	else
		position = vec3.new(
			  math.random(1.0, flocker.toroidX - 1)
			, math.random(1.0, flocker.toroidY - 1)
			, flocker.threeDeeFlocking and math.random(1.0, flocker.toroidZ) or 0
			)
	end

	if direction then
		direction = vec3.newV(direction)
	else
		direction = vec3.vecFromDeg(
			  math.random(0.0, 359.9999)
			, flocker.threeDeeFlocking and math.random(0.0, 180) or nil
			)
	end

	health 		= health or flocker.states.parameters.health.start
	age 		= age or flocker.states.parameters.age.start

	local randomSelect = math.floor(math.random(1, table.maxn( flocker.birdTypes )) )
	birdType 	= birdType or flocker.birdTypes[ randomSelect ]

	local newBird =
	{	-- choose random position and direction
		  p 	= position 	-- position
		, d 	= direction -- direction
		, a		= age 		-- age of the bird in milliseconds
		, h		= health	-- health of the bird in hit points :)

		, typeId			= birdType.typeId
		, typeName			= birdType.typeName
		, color 			= birdType.color
		, messageQueue 		= {} -- interbirdnet support
	}

	-- decorate the bird with the required functions
	flocker.states.SetState( newBird, birdType.initialState, birdType.optionalParameters )

		--[[Add the common functions to the bird for simple access
			Only need to do this here, not in the call to SetState
			(which would be wasteful unless we are dynamically updating those functions.)
		]]
	for name, fn in pairs( flocker.states ) do
		if "function" == type(fn) then newBird[name] = fn end
	end


	-- set the initial age and some properties used by display code
	newBird:AgeBird()

	--table.insert( flocker.birds, newBird )
	flocker.birds[newBird] = newBird
	flocker.birdCount = flocker.birdCount + 1
	return newBird
end



